//***************************************************************************************
//
//! \file cmd_client.c
//! Implementation of the commands in the ftp client.
//!
//! \author    wgy
//! \version   V1.0
//! \date      2022-11-13
//! \copyright GNU Public License V3.0
//
//***************************************************************************************
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <fcntl.h>
#include "ftp_client.h"
#include "cmd_client.h"
/// @brief 在客户端展示帮助信息。
void show_help()
{
	printf("\033[32mhelp\033[0m\t--print this command list\n");
	// printf("\033[32mopen\033[0m\t--open the server\n");
	// printf("\033[32mclose\033[0m\t--close the connection with the server\n");
	printf("\033[32mmkdir\033[0m\t--make new dir on the ftp server\n");
	printf("\033[32mrmdir\033[0m\t--delete the dir on the ftp server\n");
	printf("\033[32mdelete\033[0m\t--delete the file on the ftp server\n");
	printf("\033[32mpwd\033[0m\t--print the current directory of server\n");
	printf("\033[32mls\033[0m\t--list the files and directoris in current directory of server\n");
	printf("\033[32mcd [directory]\033[0m\n\t--enter  of server\n");
	printf("\033[32mmode\033[0m\n\t--change current mode, PORT or PASV\n");
	printf("\033[32mput [local_file] \033[0m\n\t--send [local_file] to server as \n");
	printf("\tif  isn't given, it will be the same with [local_file] \n");
	printf("\tif there is any \' \' in , write like this \'\\ \'\n");
	printf("\033[32mget [remote file] \033[0m\n\t--get [remote file] to local host as\n");
	printf("\tif  isn't given, it will be the same with [remote_file] \n");
	printf("\tif there is any \' \' in , write like this \'\\ \'\n");
	printf("\033[32mlpwd\033[0m\t--print the current directory of local host\n");
	printf("\033[32mlls\033[0m\t--list the files and directoris in current directory of local host\n");
	printf("\033[32mlcd [directory]\033[0m\n\t--enter  of localhost\n");
	printf("\033[32mquit\033[0m\t--quit this ftp client program\n");
}
/// @brief 读取客户端命令后的文件名字。
/// @param user_cmd 客户端命令
/// @param src_file 全路径名
/// @param dst_file 文件名
void ftp_cmd_filename(char *user_cmd, char *src_file, char *dst_file)
{
	int length, flag = 0;

	char *cmd_src;
	cmd_src = strchr(user_cmd, ' ');
	if (cmd_src == NULL)
	{
		printf("Command error!\n");
		return;
	}
	else
	{
		while (*cmd_src == ' ')
			cmd_src++;
	}

	int i = 0, j = -1;
	if (cmd_src == NULL || *cmd_src == '\0')
	{
		printf("Command error!\n");
	}
	else
	{
		length = strlen(cmd_src);
		while (i <= length)
		{
			src_file[i] = *(cmd_src + i);
			if (src_file[i] == '/')
				j = i;
			i++;
		}
	}
	i = i - 2;
	while (src_file[i] == ' ')
		i--;
	if (src_file[i + 1] == ' ')
		src_file[i + 1] = '\0';
	length = strlen(src_file);
	i = 0;
	if (j < 0)
		strcpy(dst_file, src_file);
	else
	{
		j++;
		while (j + i <= length)
		{
			dst_file[i] = src_file[i + j];
			i++;
		}
	}
}
/// @brief 提示用户选择文件传输模式。支持ASCII和Binary。
void choose_file_type(Msg *msg)
{
	printf("choose get file type:[B/a](binary or ASCII) ");
	char file_type[3];
	fgets(file_type, 3, stdin);
	fflush(stdin);
	if (file_type[0] == 'a')
	{

		msg->file_type = ASCII;
	}
	else
	{
		msg->file_type = BINARY;
	}
}
/// @brief PUT命令
void ftp_put(Msg *msg)
{
	int count = 0, i = 0;

	char src_file[512];
	char dst_file[512];
	ftp_cmd_filename(msg->control_info, src_file, dst_file);
	struct stat file_info;
	if (stat(src_file, &file_info) < 0)
	{
		printf("Local file %s doesn't exist!\n", src_file);
		return;
	}

	choose_file_type(msg);

	int local_file = open(src_file, O_RDONLY);
	if (local_file < 0)
	{
		printf("Open local file %s error!\n", dst_file);
		return;
	}

	ftp_send_cmd("STOR ", dst_file, msg->ftp_control_sock);
	ftp_get_reply(msg->ftp_control_sock);

	int size;
	struct stat file_stat;
	int ret = fstat(local_file, &file_stat); // 获取文件状态
	if (ret == -1)
	{
		printf("Get file %s stat failed:%s\n", dst_file, strerror(errno));
		close(local_file);
		return;
	}
	size = file_stat.st_size;
	char s[50];
	sprintf(s, "%d\0\0", size);
	ftp_send_cmd("SIZE ", s, msg->ftp_control_sock);

	if (msg->file_type == BINARY)
	{
		char send_buf[MAX_INFO];
		while (1)
		{
			count = read(local_file, send_buf, MAX_INFO);
			if (count <= 0)
				break;
			else
			{
				// printf("count:%d\n%s",count, send_buf);
				if ((write(msg->ftp_data_sock, send_buf, count)) != count)
				{
					printf("put file wrong. count:%d\n", count);
				}
			}
		}
	}
	else if (msg->file_type == ASCII)
	{
		FILE *f = fopen(src_file, "rt");
		if (f == NULL)
			printf("wrong");
		char c;
		while ((c = fgetc(f)) != EOF)
		{
			write(msg->ftp_data_sock, &c, 1);
		}
	}
	close(local_file);
	close(msg->ftp_data_sock);
	printf("put file successfully\n");
	ftp_get_reply(msg->ftp_control_sock);
}
/// @brief GET命令
void ftp_get(Msg *msg)
{
	char src_file[512];
	char dst_file[512];
	ftp_cmd_filename(msg->control_info, src_file, dst_file);

	choose_file_type(msg);
	char file_mode[3];
	file_mode[0] = 'w';
	if (msg->file_type == BINARY)
	{
		file_mode[1] = 'b';
		file_mode[2] = '\0';
	}
	else if (msg->file_type == ASCII)
	{
		file_mode[1] = 't';
		file_mode[2] = '\0';
	}

	int flag = 0;
	struct stat file_info;
	if (!stat(dst_file, &file_info))
	{
		printf("local file %s exists: %d bytes\n", dst_file, (int)file_info.st_size);
		printf("Do you want to cover it? [y/n]");
		char cover_flag[3];
		fgets(cover_flag, sizeof(cover_flag), stdin);

		fflush(stdin);
		if (cover_flag[0] != 'y' && cover_flag[0] != 'Y')
		{
			return;
		}
		else
		{
			flag = 1;
		}
	}

	if (msg->file_type == BINARY)
		ftp_send_cmd("RETR b ", src_file, msg->ftp_control_sock);
	else if (msg->file_type == ASCII)
		ftp_send_cmd("RETR a ", src_file, msg->ftp_control_sock);
	static int reply_code = 0, count = 0;
	char rcv_buf_[MSG_INFO];
	memset(rcv_buf_, 0, MSG_INFO);

	count = read(msg->ftp_control_sock, rcv_buf_, MSG_INFO);
	
	char *msg_ = rcv_buf_ + 3;
	*msg_ = '\0';

	if (550 == atoi(rcv_buf_))
	{
		int a = 0;
		msg_ +=1;
		if(strncmp(msg_+strlen(msg_)-9,"task done",9) == 0){
			*(msg_ + strlen(msg_) -9 ) = '\0';
			a = 1;
		}
		printf("%s", msg_);
		if(a == 0) ftp_get_reply(msg->ftp_control_sock);
		return;
	}

	if(flag) printf("Get file %s aborted.\n", src_file);

	char s[50];
	ftp_send_cmd("SIZE ", src_file, msg->ftp_control_sock);
	int num = recv(msg->ftp_control_sock, s, 50, 0);
	int size = atoi(s);
	
	FILE *fp = fopen(dst_file, file_mode);
	if (fp == NULL)
	{
		printf("Open file error: %s\r\n", strerror(errno));
		fclose(fp);
		return;
	}
	int local_file;
	local_file = fileno(fp);
	if (local_file < 0)
	{
		printf("Create local file %s error!\n", dst_file);
		return;
	}

	char rcv_buf[MAX_INFO + 1];
	memset(rcv_buf, 0, MAX_INFO + 1);

	while ((count = read(msg->ftp_data_sock, rcv_buf, MAX_INFO > size ? size : MAX_INFO)) > 0)
	{
		size -= count;
		write(local_file, rcv_buf, count);
		if (size <= 0)
			break;
	}

	fclose(fp);
	umask(0);
	if (!chmod(src_file, 0777))
		printf("get %s to local!\n", dst_file);
	else
		printf("Chmod %s to 0777 error!\n", dst_file);

	close(msg->ftp_data_sock);
	ftp_get_reply(msg->ftp_control_sock);
}
/// @brief LS命令
void ftp_list(Msg *msg)
{
	ftp_send_cmd("LIST", NULL, msg->ftp_control_sock);
	ftp_get_reply(msg->ftp_control_sock);
	ftp_get_reply(msg->ftp_data_sock);
	close(msg->ftp_data_sock);
}
/// @brief PWD命令
void ftp_pwd(Msg *msg)
{
	ftp_send_cmd("PWD", NULL, msg->ftp_control_sock);
	ftp_get_reply(msg->ftp_control_sock);
}
/// @brief CD命令
void ftp_cd(Msg *msg)
{
	char *cmd = strchr(msg->control_info, ' ');

	char path[1024];

	if (cmd == NULL)
	{
		printf("Command error!\n");
		return;
	}
	else
	{
		while (*cmd == ' ')
			cmd++;
	}

	if (cmd == NULL || cmd == "\0")
	{
		printf("Command error!\n");
		return;
	}
	else
	{
		strncpy(path, cmd, strlen(cmd));
		path[strlen(cmd)] = '\0';
		ftp_send_cmd("CWD ", path, msg->ftp_control_sock);
		ftp_get_reply(msg->ftp_control_sock);
	}
}
/// @brief QUIT命令
void ftp_quit(Msg *msg)
{
	ftp_send_cmd("QUIT", NULL, msg->ftp_control_sock);
	ftp_get_reply(msg->ftp_control_sock);
}
/// @brief DELETE命令
void ftp_delete(Msg *msg)
{
	char *cmd = strchr(msg->control_info, ' ');

	char filename[1024];

	if (cmd == NULL)
	{
		printf("Command error!\n");
		return;
	}
	else
	{
		while (*cmd == ' ')
			cmd++;
	}

	for (int i = strlen(cmd) - 1; i >= 0; i--)
	{
		if (cmd[i] == ' ' || cmd[i] == '\n')
			cmd[i] = '\0';
		else
			break;
	}

	if (cmd == NULL || cmd == '\0')
	{
		printf("Command error!\n");
		return;
	}
	else
	{
		strncpy(filename, cmd, strlen(cmd));
		filename[strlen(cmd)] = '\0';
		ftp_send_cmd("DELE ", filename, msg->ftp_control_sock);
		ftp_get_reply(msg->ftp_control_sock);
	}
}
/// @brief MKDIR命令
void ftp_mkdir(Msg *msg)
{
	char *cmd = strchr(msg->control_info, ' ');
	if (cmd == NULL)
	{
		printf("Command error!\n");
		return;
	}
	else
	{
		while (*cmd == ' ')
			cmd++;
	}

	int len = strlen(cmd);
	for (int i = len - 1; i >= 0; i--)
	{
		if (cmd[i] == '\n' || cmd[i] == ' ')
		{
			cmd[i] = '\0';
		}
		else
		{
			break;
		}
	}
	if (cmd == NULL || cmd == '\0')
	{
		printf("Command error!\n");
		return;
	}
	else
	{
		cmd[strlen(cmd)] = '\0';
		printf("mkdir：\"%s\"\n", cmd);
		ftp_send_cmd("MKD ", cmd, msg->ftp_control_sock);
		ftp_get_reply(msg->ftp_control_sock);
	}
}
/// @brief RMDIR命令
void ftp_rmdir(Msg *msg)
{
	char *cmd = strchr(msg->control_info, ' ');

	char path[1024];

	if (cmd == NULL)
	{
		printf("Command error!\n");
		return;
	}
	else
	{
		while (*cmd == ' ')
			cmd++;
	}
	if (cmd == NULL || cmd == '\0')
	{
		printf("Command error!\n");
		return;
	}

	else
	{
		strncpy(path, cmd, strlen(cmd));

		path[strlen(cmd)] = '\0';

		ftp_send_cmd("RMD ", path, msg->ftp_control_sock);
		// printf("rmdir: \"%s\".\n", path);
		ftp_get_reply(msg->ftp_control_sock);
	}
}
/// @brief 本地LLS命令
void local_list()
{
	FILE *pipe_fp;
	char t_dir[DIR_INFO];
	char list_cmd_info[DIR_INFO];
	snprintf(list_cmd_info, DIR_INFO, "ls -l %s", getcwd(t_dir, DIR_INFO));
	if ((pipe_fp = popen(list_cmd_info, "r")) == NULL)
	{
		printf("pipe open error in cmd_list\n");
		return;
	}
	char t_char;
	while ((t_char = fgetc(pipe_fp)) != EOF)
	{
		printf("%c", t_char);
	}
	pclose(pipe_fp);
}
/// @brief 本地LPWD命令
void local_pwd()
{
	char curr_dir[512];

	int size = sizeof(curr_dir);

	if (getcwd(curr_dir, size) == NULL)
		printf("Getcwd failed\n");
	else
		printf("%s\n", curr_dir);
}
/// @brief 本地LCD命令
void local_cd(char *user_cmd)
{
	char *cmd = strchr(user_cmd, ' ');

	char path[1024];

	if (cmd == NULL)
	{
		printf("Command error!\n");
		return;
	}

	else
	{
		while (*cmd == ' ')
			cmd++;
	}

	if (cmd == NULL || cmd == '\0')
	{
		printf("Command error!\n");
		return;
	}

	else
	{
		strncpy(path, cmd, strlen(cmd));

		path[strlen(cmd)] = '\0';

		if (chdir(path) < 0)
			printf("Local: chdir to %s error!\n", path);
		else
			printf("Local: chdir to %s\n", path);
	}
}
